#!/usr/bin/env python

# TODO:
# Detect if the file already exists, and only show the "use existing" in this case
# Organize the dialog box into groups
# Have an option to resize the image to the nearest power of two on export
# Detect if the specified license version is ported to the specified country
# Unify the license selection options, perhaps by expanding the FIFF library

from array import array
import gimp
from gimpfu import *
import fiff
import os
import getpass
import gtk

def power_of_two( number ):
    return ( number != 0 ) and ( number & ( number - 1 ) == 0 )

def write_image_chunk( IDorIDs, width, height, drawable ):
    # Try beginning the image chunk
    if ( fiff.write_chunk_begin( IDorIDs ) ):
        # Write out the image's width. Height is implied based on width and the chunk size
        fiff.write_uint32( width )

        # Get all the image bytes in one fell swoop ...
        pixel_region = drawable.get_pixel_rgn( 0, 0, width, height, FALSE, FALSE )
        image_bytes = pixel_region[0:width, 0:height];        

        # and write them to the file!
        fiff.write_uint8s( image_bytes )

        # We're done! End the chunk
        fiff.write_chunk_end( IDorIDs )

def write_custom_metadata( author, license_string, creation_time ):
    if ( fiff.write_chunk_begin( "METADATA" ) ):
        if ( fiff.write_chunk_begin( "AUTHOR" ) ):
            fiff.write_string( author )
            fiff.write_chunk_end( "AUTHOR" )
        else:
            return False

        if ( fiff.write_chunk_begin( "LICENSE" ) ):
            fiff.write_string( license_string )
            fiff.write_chunk_end( "LICENSE" )
        else:
            return False

        # Either write existing creation time if we're overwriting a file or a new time
        if ( fiff.write_chunk_begin( "CR TIME" ) ):
            if ( creation_time != None ):
                fiff.write_uint64( creation_time )
            else:
                fiff.write_uint64( fiff.get_fiff_epoch_timestamp() )
            fiff.write_chunk_end( "CR TIME" )
        else:
            return False

        if ( fiff.write_chunk_begin( "MOD TIME" ) ):
            fiff.write_uint64( fiff.get_fiff_epoch_timestamp() )
            fiff.write_chunk_end( "MOD TIME" )
        else:
            return False
    
        fiff.write_chunk_end( "METADATA" )

    return True

def save_avionTex(img, drawable, filename, http_formatted_filename_parameter):

    # Verify that the image CAN be saved
    # If not:
    width, height = img.width, img.height
    if ( power_of_two( width ) == False or power_of_two( height ) == False ):
        # Complain and offer suggestions to fix it
        gtk.MessageDialog( None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, type = gtk.MESSAGE_ERROR,
                           message_format = "Sorry, can't save: image dimensions must powers of two." )
        return

    dialog = gtk.Dialog( "Options", None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                         ( gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT) )

    use_existing_checkbutton = gtk.CheckButton("Use Existing File Metadata")
    dialog.vbox.pack_start( use_existing_checkbutton )
    use_existing_checkbutton.set_active( True )
    use_existing_checkbutton.show()

    author_label = gtk.Label("Author")
    dialog.vbox.pack_start( author_label )
    author_label.show();

    author_entry = gtk.Entry()
    dialog.vbox.pack_start( author_entry )
    author_entry.set_text( getpass.getuser() )
    author_entry.show()

    license_label = gtk.Label("License")
    dialog.vbox.pack_start( license_label )
    license_label.show()

    license_selector_combobox = gtk.combo_box_new_text()
    license_selector_combobox.append_text( "All Rights Reserved" )
    license_selector_combobox.append_text( "Public Domain Released" )
    license_selector_combobox.append_text( "Public Domain" )
    license_selector_combobox.append_text( "CC0" )
    license_selector_combobox.append_text( "CC BY" )
    license_selector_combobox.append_text( "CC BY-SA" )
    license_selector_combobox.append_text( "CC BY-ND" )
    license_selector_combobox.append_text( "CC BY-NC" )
    license_selector_combobox.append_text( "CC BY-NC-SA" )
    license_selector_combobox.append_text( "CC BY-NC-ND" )
    license_selector_combobox.set_active( 5 )
    dialog.vbox.pack_start( license_selector_combobox )
    license_selector_combobox.show()

    version_label = gtk.Label("Version")
    dialog.vbox.pack_start( version_label )
    version_label.show()
    
    version_combobox = gtk.combo_box_new_text()
    version_combobox.append_text( "1.0" )
    version_combobox.append_text( "2.0" )
    version_combobox.append_text( "2.5" )
    version_combobox.append_text( "3.0" )
    version_combobox.append_text( "4.0" )
    version_combobox.set_active( 3 )
    dialog.vbox.pack_start( version_combobox )
    version_combobox.show();
    
    jurisdiction_label = gtk.Label("Jurisdiction")
    dialog.vbox.pack_start( jurisdiction_label )
    jurisdiction_label.show();

    jurisdiction_combobox = gtk.combo_box_new_text()
    jurisdiction_combobox.append_text( "None" )
    jurisdiction_combobox.append_text( "ABW" )
    jurisdiction_combobox.append_text( "AFG" )
    jurisdiction_combobox.append_text( "AGO" )
    jurisdiction_combobox.append_text( "AIA" )
    jurisdiction_combobox.append_text( "ALA" )
    jurisdiction_combobox.append_text( "ALB" )
    jurisdiction_combobox.append_text( "AND" )
    jurisdiction_combobox.append_text( "ARE" )
    jurisdiction_combobox.append_text( "ARG" )
    jurisdiction_combobox.append_text( "ARM" )
    jurisdiction_combobox.append_text( "ASM" )
    jurisdiction_combobox.append_text( "ATA" )
    jurisdiction_combobox.append_text( "ATF" )
    jurisdiction_combobox.append_text( "ATG" )
    jurisdiction_combobox.append_text( "AUS" )
    jurisdiction_combobox.append_text( "AUT" )
    jurisdiction_combobox.append_text( "AZE" )
    jurisdiction_combobox.append_text( "BDI" )
    jurisdiction_combobox.append_text( "BEL" )
    jurisdiction_combobox.append_text( "BEN" )
    jurisdiction_combobox.append_text( "BES" )
    jurisdiction_combobox.append_text( "BFA" )
    jurisdiction_combobox.append_text( "BGD" )
    jurisdiction_combobox.append_text( "BGR" )
    jurisdiction_combobox.append_text( "BHR" )
    jurisdiction_combobox.append_text( "BHS" )
    jurisdiction_combobox.append_text( "BIH" )
    jurisdiction_combobox.append_text( "BLM" )
    jurisdiction_combobox.append_text( "BLR" )
    jurisdiction_combobox.append_text( "BLZ" )
    jurisdiction_combobox.append_text( "BMU" )
    jurisdiction_combobox.append_text( "BOL" )
    jurisdiction_combobox.append_text( "BRA" )
    jurisdiction_combobox.append_text( "BRB" )
    jurisdiction_combobox.append_text( "BRN" )
    jurisdiction_combobox.append_text( "BTN" )
    jurisdiction_combobox.append_text( "BVT" )
    jurisdiction_combobox.append_text( "BWA" )
    jurisdiction_combobox.append_text( "CAF" )
    jurisdiction_combobox.append_text( "CAN" )
    jurisdiction_combobox.append_text( "CCK" )
    jurisdiction_combobox.append_text( "CHE" )
    jurisdiction_combobox.append_text( "CHL" )
    jurisdiction_combobox.append_text( "CHN" )
    jurisdiction_combobox.append_text( "CIV" )
    jurisdiction_combobox.append_text( "CMR" )
    jurisdiction_combobox.append_text( "COD" )
    jurisdiction_combobox.append_text( "COG" )
    jurisdiction_combobox.append_text( "COK" )
    jurisdiction_combobox.append_text( "COL" )
    jurisdiction_combobox.append_text( "COM" )
    jurisdiction_combobox.append_text( "CPV" )
    jurisdiction_combobox.append_text( "CRI" )
    jurisdiction_combobox.append_text( "CUB" )
    jurisdiction_combobox.append_text( "CUW" )
    jurisdiction_combobox.append_text( "CXR" )
    jurisdiction_combobox.append_text( "CYM" )
    jurisdiction_combobox.append_text( "CYP" )
    jurisdiction_combobox.append_text( "CZE" )
    jurisdiction_combobox.append_text( "DEU" )
    jurisdiction_combobox.append_text( "DJI" )
    jurisdiction_combobox.append_text( "DMA" )
    jurisdiction_combobox.append_text( "DNK" )
    jurisdiction_combobox.append_text( "DOM" )
    jurisdiction_combobox.append_text( "DZA" )
    jurisdiction_combobox.append_text( "ECU" )
    jurisdiction_combobox.append_text( "EGY" )
    jurisdiction_combobox.append_text( "ERI" )
    jurisdiction_combobox.append_text( "ESH" )
    jurisdiction_combobox.append_text( "ESP" )
    jurisdiction_combobox.append_text( "EST" )
    jurisdiction_combobox.append_text( "ETH" )
    jurisdiction_combobox.append_text( "FIN" )
    jurisdiction_combobox.append_text( "FJI" )
    jurisdiction_combobox.append_text( "FLK" )
    jurisdiction_combobox.append_text( "FRA" )
    jurisdiction_combobox.append_text( "FRO" )
    jurisdiction_combobox.append_text( "FSM" )
    jurisdiction_combobox.append_text( "GAB" )
    jurisdiction_combobox.append_text( "GBR" )
    jurisdiction_combobox.append_text( "GEO" )
    jurisdiction_combobox.append_text( "GGY" )
    jurisdiction_combobox.append_text( "GHA" )
    jurisdiction_combobox.append_text( "GIB" )
    jurisdiction_combobox.append_text( "GIN" )
    jurisdiction_combobox.append_text( "GLP" )
    jurisdiction_combobox.append_text( "GMB" )
    jurisdiction_combobox.append_text( "GNB" )
    jurisdiction_combobox.append_text( "GNQ" )
    jurisdiction_combobox.append_text( "GRC" )
    jurisdiction_combobox.append_text( "GRD" )
    jurisdiction_combobox.append_text( "GRL" )
    jurisdiction_combobox.append_text( "GTM" )
    jurisdiction_combobox.append_text( "GUF" )
    jurisdiction_combobox.append_text( "GUM" )
    jurisdiction_combobox.append_text( "GUY" )
    jurisdiction_combobox.append_text( "HKG" )
    jurisdiction_combobox.append_text( "HMD" )
    jurisdiction_combobox.append_text( "HND" )
    jurisdiction_combobox.append_text( "HRV" )
    jurisdiction_combobox.append_text( "HTI" )
    jurisdiction_combobox.append_text( "HUN" )
    jurisdiction_combobox.append_text( "IDN" )
    jurisdiction_combobox.append_text( "IMN" )
    jurisdiction_combobox.append_text( "IND" )
    jurisdiction_combobox.append_text( "IOT" )
    jurisdiction_combobox.append_text( "IRL" )
    jurisdiction_combobox.append_text( "IRN" )
    jurisdiction_combobox.append_text( "IRQ" )
    jurisdiction_combobox.append_text( "ISL" )
    jurisdiction_combobox.append_text( "ISR" )
    jurisdiction_combobox.append_text( "ITA" )
    jurisdiction_combobox.append_text( "JAM" )
    jurisdiction_combobox.append_text( "JEY" )
    jurisdiction_combobox.append_text( "JOR" )
    jurisdiction_combobox.append_text( "JPN" )
    jurisdiction_combobox.append_text( "KAZ" )
    jurisdiction_combobox.append_text( "KEN" )
    jurisdiction_combobox.append_text( "KGZ" )
    jurisdiction_combobox.append_text( "KHM" )
    jurisdiction_combobox.append_text( "KIR" )
    jurisdiction_combobox.append_text( "KNA" )
    jurisdiction_combobox.append_text( "KOR" )
    jurisdiction_combobox.append_text( "KWT" )
    jurisdiction_combobox.append_text( "LAO" )
    jurisdiction_combobox.append_text( "LBN" )
    jurisdiction_combobox.append_text( "LBR" )
    jurisdiction_combobox.append_text( "LBY" )
    jurisdiction_combobox.append_text( "LCA" )
    jurisdiction_combobox.append_text( "LIE" )
    jurisdiction_combobox.append_text( "LKA" )
    jurisdiction_combobox.append_text( "LSO" )
    jurisdiction_combobox.append_text( "LTU" )
    jurisdiction_combobox.append_text( "LUX" )
    jurisdiction_combobox.append_text( "LVA" )
    jurisdiction_combobox.append_text( "MAC" )
    jurisdiction_combobox.append_text( "MAF" )
    jurisdiction_combobox.append_text( "MAR" )
    jurisdiction_combobox.append_text( "MCO" )
    jurisdiction_combobox.append_text( "MDA" )
    jurisdiction_combobox.append_text( "MDG" )
    jurisdiction_combobox.append_text( "MDV" )
    jurisdiction_combobox.append_text( "MEX" )
    jurisdiction_combobox.append_text( "MHL" )
    jurisdiction_combobox.append_text( "MKD" )
    jurisdiction_combobox.append_text( "MLI" )
    jurisdiction_combobox.append_text( "MLT" )
    jurisdiction_combobox.append_text( "MMR" )
    jurisdiction_combobox.append_text( "MNE" )
    jurisdiction_combobox.append_text( "MNG" )
    jurisdiction_combobox.append_text( "MNP" )
    jurisdiction_combobox.append_text( "MOZ" )
    jurisdiction_combobox.append_text( "MRT" )
    jurisdiction_combobox.append_text( "MSR" )
    jurisdiction_combobox.append_text( "MTQ" )
    jurisdiction_combobox.append_text( "MUS" )
    jurisdiction_combobox.append_text( "MWI" )
    jurisdiction_combobox.append_text( "MYS" )
    jurisdiction_combobox.append_text( "MYT" )
    jurisdiction_combobox.append_text( "NAM" )
    jurisdiction_combobox.append_text( "NCL" )
    jurisdiction_combobox.append_text( "NER" )
    jurisdiction_combobox.append_text( "NFK" )
    jurisdiction_combobox.append_text( "NGA" )
    jurisdiction_combobox.append_text( "NIC" )
    jurisdiction_combobox.append_text( "NIU" )
    jurisdiction_combobox.append_text( "NLD" )
    jurisdiction_combobox.append_text( "NOR" )
    jurisdiction_combobox.append_text( "NPL" )
    jurisdiction_combobox.append_text( "NRU" )
    jurisdiction_combobox.append_text( "NZL" )
    jurisdiction_combobox.append_text( "OMN" )
    jurisdiction_combobox.append_text( "PAK" )
    jurisdiction_combobox.append_text( "PAN" )
    jurisdiction_combobox.append_text( "PCN" )
    jurisdiction_combobox.append_text( "PER" )
    jurisdiction_combobox.append_text( "PHL" )
    jurisdiction_combobox.append_text( "PLW" )
    jurisdiction_combobox.append_text( "PNG" )
    jurisdiction_combobox.append_text( "POL" )
    jurisdiction_combobox.append_text( "PRI" )
    jurisdiction_combobox.append_text( "PRK" )
    jurisdiction_combobox.append_text( "PRT" )
    jurisdiction_combobox.append_text( "PRY" )
    jurisdiction_combobox.append_text( "PSE" )
    jurisdiction_combobox.append_text( "PYF" )
    jurisdiction_combobox.append_text( "QAT" )
    jurisdiction_combobox.append_text( "REU" )
    jurisdiction_combobox.append_text( "ROU" )
    jurisdiction_combobox.append_text( "RUS" )
    jurisdiction_combobox.append_text( "RWA" )
    jurisdiction_combobox.append_text( "SAU" )
    jurisdiction_combobox.append_text( "SDN" )
    jurisdiction_combobox.append_text( "SEN" )
    jurisdiction_combobox.append_text( "SGP" )
    jurisdiction_combobox.append_text( "SGS" )
    jurisdiction_combobox.append_text( "SHN" )
    jurisdiction_combobox.append_text( "SJM" )
    jurisdiction_combobox.append_text( "SLB" )
    jurisdiction_combobox.append_text( "SLE" )
    jurisdiction_combobox.append_text( "SLV" )
    jurisdiction_combobox.append_text( "SMR" )
    jurisdiction_combobox.append_text( "SOM" )
    jurisdiction_combobox.append_text( "SPM" )
    jurisdiction_combobox.append_text( "SRB" )
    jurisdiction_combobox.append_text( "SSD" )
    jurisdiction_combobox.append_text( "STP" )
    jurisdiction_combobox.append_text( "SUR" )
    jurisdiction_combobox.append_text( "SVK" )
    jurisdiction_combobox.append_text( "SVN" )
    jurisdiction_combobox.append_text( "SWE" )
    jurisdiction_combobox.append_text( "SWZ" )
    jurisdiction_combobox.append_text( "SXM" )
    jurisdiction_combobox.append_text( "SYC" )
    jurisdiction_combobox.append_text( "SYR" )
    jurisdiction_combobox.append_text( "TCA" )
    jurisdiction_combobox.append_text( "TCD" )
    jurisdiction_combobox.append_text( "TGO" )
    jurisdiction_combobox.append_text( "THA" )
    jurisdiction_combobox.append_text( "TJK" )
    jurisdiction_combobox.append_text( "TKL" )
    jurisdiction_combobox.append_text( "TKM" )
    jurisdiction_combobox.append_text( "TLS" )
    jurisdiction_combobox.append_text( "TON" )
    jurisdiction_combobox.append_text( "TTO" )
    jurisdiction_combobox.append_text( "TUN" )
    jurisdiction_combobox.append_text( "TUR" )
    jurisdiction_combobox.append_text( "TUV" )
    jurisdiction_combobox.append_text( "TWN" )
    jurisdiction_combobox.append_text( "TZA" )
    jurisdiction_combobox.append_text( "UGA" )
    jurisdiction_combobox.append_text( "UKR" )
    jurisdiction_combobox.append_text( "UMI" )
    jurisdiction_combobox.append_text( "URY" )
    jurisdiction_combobox.append_text( "USA" )
    jurisdiction_combobox.append_text( "UZB" )
    jurisdiction_combobox.append_text( "VAT" )
    jurisdiction_combobox.append_text( "VCT" )
    jurisdiction_combobox.append_text( "VEN" )
    jurisdiction_combobox.append_text( "VGB" )
    jurisdiction_combobox.append_text( "VIR" )
    jurisdiction_combobox.append_text( "VNM" )
    jurisdiction_combobox.append_text( "VUT" )
    jurisdiction_combobox.append_text( "WLF" )
    jurisdiction_combobox.append_text( "WSM" )
    jurisdiction_combobox.append_text( "YEM" )
    jurisdiction_combobox.append_text( "ZAF" )
    jurisdiction_combobox.append_text( "ZMB" )
    jurisdiction_combobox.append_text( "ZWE" )
    dialog.vbox.pack_start( jurisdiction_combobox )
    jurisdiction_combobox.set_active( 0 )
    jurisdiction_combobox.show()

    response = dialog.run()
    # Do nothing and exit if the user clicked cancel
    if ( response == gtk.RESPONSE_REJECT or response == gtk.RESPONSE_CANCEL or
         response == gtk.RESPONSE_CLOSE or response == gtk.RESPONSE_DELETE_EVENT ):
        return
    
    author_to_save = author_entry.get_text()

    license_to_use = license_selector_combobox.get_active_text()
    if ( license_to_use == None ):
        license_to_use = "All Rights Reserved"

    version_to_use = version_combobox.get_active_text()
    if ( version_to_use == None ):
        version_to_use = ""

    jurisdiction = jurisdiction_combobox.get_active_text()
    if ( jurisdiction == None or jurisdiction == "None" ):
        jurisdiction = ""

    license_string = license_to_use
    if ( license_to_use[:2] == "CC" ):
        if ( version_to_use != "" ):
            license_string = license_string + " " + version_to_use
        if ( jurisdiction != "" ):
            license_string = license_string + " " + jurisdiction

    creation_time = None
    if ( use_existing_checkbutton.get_active() ):
        if ( fiff.file_begin( filename, "kExisting" ) ):
            if ( fiff.read_object_begin( ( "avionTex", "aTex" ) ) ):
                if ( fiff.get_chunk_metadata_author() != None ):
                    author_to_save = fiff.get_chunk_metadata_author()
                    license_string = fiff.get_chunk_metadata_license()
                    creation_time = fiff.get_chunk_metadata_creation_time()
                fiff.read_object_end( ( "avionTex", "aTex" ) )
            fiff.file_end()

    dialog.destroy()


    new_image = pdb["gimp-image-duplicate"]( img )

    # Try to open the file
    if ( fiff.file_begin( filename, "kNewOrOverwrite" ) ):
        # Try to start the object chunk
        texture_name = os.path.splitext( os.path.basename( filename ) )[0]
        if ( fiff.write_object_begin( ( "avionTex", "aTex" ), texture_name ) ):
            # Try to write metadata
            if ( write_custom_metadata( author_to_save, license_string, creation_time ) ):
                # Try to write the image as an Image chunk
                merged_layer = new_image.merge_visible_layers( EXPAND_AS_NECESSARY )
                merged_layer.add_alpha()
                write_image_chunk( ( "Image", "Img" ), width, height, merged_layer )

                # If the image is big enough:
                old_interpolation = pdb["gimp-context-get-interpolation"]()
                pdb["gimp-context-set-interpolation"]( 3 ) # LANCZOS
                # While the image width and height are both greater than 1
                while ( width > 1 and height > 1 ):
                    # Scale the image down by half
                    width = width // 2
                    height = height // 2
                    pdb["gimp-image-scale"]( new_image, width, height )
                    # Try to write the image as a MipLevel chunk
                    merged_layer = new_image.merge_visible_layers( EXPAND_AS_NECESSARY )
                    merged_layer.add_alpha()
                    write_image_chunk( ( "MIPLevel", "MLvl" ), width, height, merged_layer )
                pdb["gimp-context-set-interpolation"]( old_interpolation )
            fiff.write_object_end( ( "avionTex", "aTex" ) )
        fiff.file_end()
    gimp.delete( new_image )

def register_save_handlers():
    gimp.register_save_handler("file-avionTex-save", "avionTex", "")

register(
    "file-avionTex-save", # Name
    "Save as avionTex (.avionTex) file", # Blurb
    "Saves an image in the Avion Texture format, with built-in MIP maps.", # Help
    "Mark W. Gabby II", # Author
    "Mark W. Gabby II", # Copyright
    "2011", # Date
    "Avion Texture", # Menu Path?
    "RGB*", # Image Types (All?)
    [   #input args. Format (type, name, description, default [, extra])
        (PF_IMAGE, "image", "Input image", None),
        (PF_DRAWABLE, "drawable", "Input drawable", None),
        (PF_STRING, "filename", "The name of the file", None),
        (PF_STRING, "raw-filename", "The name of the file", None),
    ],
    [], #results. Format (type, name, description)
    save_avionTex, #callback
    on_query=register_save_handlers,
    menu = "<Save>")

main()
